package org.gpp.proj1.communication.protocol;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import org.gpp.proj1.logic.core.board.FacingSides;
import org.gpp.proj1.logic.core.board.RobotPosition;
import org.gpp.proj1.logic.metadata.RobotPartCategory;

public class ProtocolMessageParser {

	private static final int ATTRIBUTES_PER_ROBOT = 6;

	/**
	 * Parses a WHOSHERE protocol message, returning the clientid->alias mapping
	 * 
	 * WHOSHERE Message format:
	 * 
	 * <numclients> <clientid_1> <clientalias_1> ... <clientid_n> <clientalias_n>
	 * <numreadyclients> <readyclient1> ... <readyclientn>
	 *  
	 * @param whosHereMessage
	 * @return The clientid->alias mapping
	 */
	public static Map<String, String> parseAliasMap( Message whosHereMessage ) {

		if( ! whosHereMessage.getIdentifier().equals( ProtocolIdentifiers.WHOSHERE ) ) {
			throw new IllegalArgumentException("Invalid message type");
		}

		Map<String, String> aliasMap = new TreeMap<String, String>();

		int numClients = Integer.parseInt( whosHereMessage.getParameters()[0] );
		for( int k = 0, i = 1; k < numClients; k++, i+= 2 ) {
			aliasMap.put( whosHereMessage.getParameters()[i], whosHereMessage.getParameters()[i+1] );
		}

		return aliasMap;
	}

	/**
	 * Parses a WHOSHERE protocol message, returning the ready clietn set
	 * 
	 * WHOSHERE Message format:
	 * 
	 * <numclients> <clientid_1> <clientalias_1> ... <clientid_n> <clientalias_n>
	 * <numreadyclients> <readyclient1> ... <readyclientn>
	 *  
	 * @param whosHereMessage
	 * @return The clientid->alias mapping
	 */
	public static Set<String> parseReadySet( Message whosHereMessage ) {

		if( ! whosHereMessage.getIdentifier().equals( ProtocolIdentifiers.WHOSHERE ) ) {
			throw new IllegalArgumentException("Invalid message type");
		}

		Set<String> readySet = new TreeSet<String>();

		int numClients = Integer.parseInt( whosHereMessage.getParameters()[ ProtocolParameters.WHOSHERE_MESSAGE_NUMCLIENTS ] );

		int startIndex = numClients * 2 + 1;

		int numReadyClients = Integer.parseInt( whosHereMessage.getParameters()[startIndex] );

		for( int k = startIndex + 1; k < startIndex + numReadyClients + 1; k++ ) {
			readySet.add( whosHereMessage.getParameters()[k] );
		}

		return readySet;
	}

	/**
	 * Parses a MYROBOTDATA message, returning the robot data array
	 * 
	 * MYROBOTDATA message format:
	 * 
	 * <alias> <numrobots> <robotsName> <robothead> <torso> <armLeft> <armRight> <legs> ...
	 */
	public static String[] parseMyRobotData( Message myRobotDataMessage ) {

		if( ! myRobotDataMessage.getIdentifier().equals( ProtocolIdentifiers.MYROBOTDATA ) ) {
			throw new IllegalArgumentException("Invalid message type");
		}

		int numRobots = Integer.parseInt( myRobotDataMessage.getParameters()[ ProtocolParameters.MYROBOTLIST_NUMBER_OF_ROBOTS ] );

		String[] robotData = new String[ numRobots * ATTRIBUTES_PER_ROBOT + 1 ];

		robotData[0] = myRobotDataMessage.getParameters()[ ProtocolParameters.MYROBOTLIST_NUMBER_OF_ROBOTS ];
		for( int k = 1; k < robotData.length; k++ ) {
			robotData[k] = myRobotDataMessage.getParameters()[ k + 1 ];
		}

		return robotData;
	}

	/**
	 * Parses a PLAYERROBOTDATA or MYROBOTDATA message, returning a map with the part mapping for each robot
	 *
	 * PLAYERROBOTDATA message format:
	 * 
	 * <clientid> <numrobots> <robotsName> <robothead> <torso> <armLeft> <armRight> <legs> ...
	 */
	public static Map<String, Map<RobotPartCategory, String>> parseRobotData( Message message ) {
		if( ! message.getIdentifier().equals( ProtocolIdentifiers.MYROBOTDATA ) &&
				! message.getIdentifier().equals( ProtocolIdentifiers.PLAYEROBOTDATA )) {
			throw new IllegalArgumentException("Invalid message type");
		}

		Map< String, Map< RobotPartCategory, String > > robotDataMapping = new HashMap<String, Map<RobotPartCategory,String>>();

		int numRobots = Integer.parseInt( message.getParameters()[ ProtocolParameters.PLAYERROBOTDATA_NUMROBOTS ] );

		String[] data = message.getParameters();
		for( int k = 0; k < numRobots; k++ ) {

			String robotName = data[k * ATTRIBUTES_PER_ROBOT + 2];

			Map<RobotPartCategory, String> partMapping = new HashMap<RobotPartCategory, String>();

			partMapping.put( RobotPartCategory.HEAD, data[k * ATTRIBUTES_PER_ROBOT + 3] );
			partMapping.put( RobotPartCategory.TORSO, data[k * ATTRIBUTES_PER_ROBOT + 4] );
			partMapping.put( RobotPartCategory.ARM_LEFT, data[k * ATTRIBUTES_PER_ROBOT + 5] );
			partMapping.put( RobotPartCategory.ARM_RIGHT, data[k * ATTRIBUTES_PER_ROBOT + 6] );
			partMapping.put( RobotPartCategory.LEGS, data[k * ATTRIBUTES_PER_ROBOT + 7] );

			robotDataMapping.put( robotName, partMapping );
		}

		return robotDataMapping;
	}

	/**
	 * Parses a GAMEBOARDDATA message, returning the client->robot->initial position
	 * mapping generated by the server
	 * 
	 * The GAMEBOARDDATA format:
	 * 
	 * <numclients> <clientid> <numrobots> <robotname> <robotfacing> <robotx> <roboty> ...
	 * 
	 * @param message
	 * @return
	 */
	public static Map<String, Map<String, RobotPosition>> parseGameBoard( Message message ) {
		if( ! message.getIdentifier().equals( ProtocolIdentifiers.GAMEBOARDDATA ) ){
			throw new IllegalArgumentException("Invalid message type");
		}
		
		Map<String, Map<String, RobotPosition>> boardPositions = new HashMap<String, Map<String,RobotPosition>>();
		
		String[] data = message.getParameters();
		
		int position = 0;
		while( position < data.length - 1 ) {
			String clientID = data[ position + 1 ];
			
			boardPositions.put( clientID, new HashMap<String, RobotPosition>() );
			
			int numRobots = Integer.parseInt( data[ position + 2 ] );
			
			for (int robot = 0; robot < numRobots; robot++) {				
				String robotName = data[ position + 3 + robot * 4  ];
				FacingSides facing = FacingSides.fromValue( Integer.parseInt( data[ position + 3 + robot * 4 + 1 ] ) );
				int x = Integer.parseInt( data[ position + 3 + robot * 4 + 2 ] );
				int y = Integer.parseInt( data[ position + 3 + robot * 4 + 3 ] );
				
				boardPositions.get( clientID ).put( robotName, new RobotPosition( facing, x, y ) );
			}
			
			position += 2 + numRobots * 4;
		}
				
		return boardPositions;
	}
}
